programming4us
           
 
 
SQL Server

Implementing SQL Server Objects Using Managed Code (part 1)

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
11/14/2010 4:04:24 PM
Let’s examine how various SQL Server database objects can be implemented using managed code. You can use managed code to implement the following objects in SQL Server:
  • Stored procedures

  • Functions (scalar and table-valued)

  • Aggregates

  • Triggers

  • User-defined data types

When creating stored procedures and functions you can accept input parameters and use output parameters to return data. You can also use return values for functions and stored procedures. When creating a CLR stored procedure, it can return nothing or return an integer representing the return code. Functions can return a single value of any type. When creating your CLR objects you can access SQL Server data from the calling context. This is known as using the context connection. The context connection provides you with access to the immediate execution environment, in other words, login credentials and selected database.

No matter what database object you choose to create, you are likely to use CLR integration custom attributes. What are custom attributes? In the context of .NET Framework development, custom attributes are keywords that mark a certain piece of code. The execution environment (in this case SQL Server) examines the attributes and provides services to your code based on these attributes. Although you don’t need to know all the attributes that can be applied to each type of CLR object, it is worth understanding the concept of attributes. For example, the SqlFunction attribute indicates that the procedure marked with it is a SQL Server function. For the SqlFunction attribute you can specify multiple properties like DataAccess, Name, and TableDefinition. In this particular example, the DataAccessTableDefinition property defines the columns in the table returned by a table-valued function. property specifies whether the function will read data from the SQL Server context connection, or will not access SQL Server data at all; the Name property is the name that will be used to register the function with SQL Server; and the

Tip

You don’t need to memorize the list of attributes and their available properties. The one key attribute to remember is DataAccess. This attribute can be set to DataAccessKind.None or DataAccessKind.Read. Remember that you must specify DataAccess=DataAccessKind.Read if your CLR object has to execute queries and read data from the context connection. Examples later in this chapter show how this is implemented.


Creating CLR Functions

As explained earlier in this book, user-defined functions are compiled routines that usually perform calculations and return a single value. Functions can be defined using Transact-SQL or any .NET language. Let’s take a brief tour of creating functions using managed code. Note that while the examples will be presented in C# and Visual Basic .NET, you can create them using any .NET language.

When creating a function you should first determine whether it is a scalar or a table-valued function. Scalar functions return a single value of a SQL data type like bit or int. Table-valued functions return an expression that can be treated by the calling code as a SQL table comprising of columns and rows. All CLR-integrated user-defined functions are implemented as static methods of a class that is hosted by an assembly (see “Understanding Classes, Assemblies, Namespaces, and the GAC” earlier in this chapter). You can use the SqlFunction custom attribute to specify additional information about your function. Examples 1 and 2 show a very simple function named SayHello that returns the value “Hello world!”

Example 1. C#
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(DataAccess=DataAccessKind.None)]
public static SqlString SayHello()
{
return new SqlString("Hello world!");
}
}

Example 2. Visual Basic .NET
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Public Class UserDefinedFunctions
<Microsoft.SqlServer.Server.SqlFunction()> _
Public Shared Function SayHello() As SqlString
Return New SqlString("Hello world!")
End Function
End Class

For those who are new to .NET programming, let’s go through the elements of this code. It is worthy of mention that the two examples are absolutely equivalent, and do the same thing—return the value “Hello world!” when called. If you remember when we discussed managed code, both examples will compile to exactly the same set of MSIL statements.

The first set of lines are the using or imports directives. These directives tell the class that it will be using objects from the namespaces mentioned, including the Microsoft.SqlServer.Server namespace. This is true—we will be using the SqlFunction custom attribute that belongs in the Microsoft.SqlServer.Server namespace. We will also use the SqlString data type that belongs in the System.Data.SqlTypes namespace.

The second line declares the class. In this case, the class is called UserDefined-Functions. Note that the class doesn’t have to be called UserDefinedFunctions; you could use any name—for example, MyFunctions or SqlStuff. Remember that classes are individual logical entities and have properties and methods. Notice that the class end needs to be specified by a close brace (}) in C# or the keywords End Class in Visual Basic .NET.

Once the class is declared, we implement the actual SQL function as a method of our class. To specify that the method should be registered as a SQL function we use the SqlFunction custom attribute. We also specify that the SqlFunction attribute has its DataAccess property set to DataAccess.None—our function will not be accessing SQL Server data.

The method that is marked as SqlFunction is called SayHello. It is marked as public, meaning that it can be accessed externally—for example, from SQL Server. It is also marked as static in C# or Shared in Visual Basic .NET. These keywords indicate that the method can be called directly, without creating an instance of the class. You must mark methods that will become SQL Server functions, stored procedures, and aggregates as static/Shared for them to work properly. Our method takes no parameters as it has two empty round brackets after it, and returns a value of type SqlString (text). Inside the body of the method we simply use the returnSqlString containing the value “Hello world!” When this function is called, it will return “Hello world!” keyword to return a new

Tip

Remember that all SQL objects are written as methods of a class. They must be marked as static in C# or Shared in Visual Basic .NET. They must also be marked as public, or else the SQL Server execution environment will not be able to access them.


Table 1 lists all properties applicable to the SqlFunction custom attribute. You don’t need to memorize all of these properties—the table is provided for you to get an idea of what type of control is available for your user-defined CLR functions.

Table 1. SqlFunction Custom Attribute Properties
PropertyExplanation
IsDeterministicTrue if the function will always return the same value, if called with the same parameters. False if the function can return different values when called with the same parameters. An example of a deterministic function is the SayHello function. An example of a nondeterministic function is a function that will use database data that varies.
DataAccessSpecifies whether the function will read data from the context SQL Server connection. Acceptable values are DataAccessKind.None and DataAccessKind.Read.
SystemDataAccessSpecifies whether the function will require access to system catalogs or system tables in SQL Server—for example, sys.object. Acceptable values are System DataAccessKind.None and SystemDataAccessKind. Read.
IsPreciseSpecifies whether the function will be performing imprecise computation, for example, float operations. True if the function does not involve these operations, false if it does. SQL Server uses this information for indexing.
FillRowMethodNameUsed for table-valued functions. This is the name of a method in the same class that serves as the table-valued function contract.
TableDefinitionUsed for table-valued functions. Defines the column list in the table returned.
NameThe name of the SQL function.

Here are a few examples of more complex user-defined functions. Look through these functions to understand how they are implemented. When looking through each function ask yourself if the function should be registered with the permission set of SAFE, EXTERNAL_ACCESS, or UNSAFE.

Registering CLR Functions

You must register CLR functions with a SQL Server database before they can be used. To do this, first register the assembly containing the function using CREATE ASSEMBLY. Then, use the CREATE FUNCTION statement with EXTERNAL NAME parameter to register the function for use. Use the syntax shown in Example 3 to register the function.

Example 3. Syntax—Registering CLR Functions
CREATE FUNCTION dbo.function_name (@param1 as param1_data_type,
@param2 as param2_data_type)
RETURNS return_data_type
AS EXTERNAL NAME assembly_name.class_name.function_name;

Note that parameters and return types must be specified when registering the user-defined CLR function. In Examples 4 and 5 we will register the SayHello function we have created earlier in this article. The SayHello function takes no parameters, and returns a value of type SqlString. The SqlString type is equivalent of the nvarchar SQL Server data type.

Example 4. Registering a Simple CLR Function
CREATE FUNCTION dbo.SayHello()
RETURNS nvarchar(max)
AS EXTERNAL NAME MyAwesomeFunctions.UserDefinedFunctions.SayHello;

Example 5. Calling a Simple CLR Function
Select dbo.SayHello()
--RESULTS:
-------------
Hello world!
(1 row(s) affected)

Creating CLR Aggregates

Aggregates are functions that operate on a set of values and return a single value. Examples of built-in aggregate functions are SUM, AVG, and MAX. In your query, you will usually pass a column to these aggregate functions and will be returned the total, average, or maximum value of all the rows in that column. Previously, SQL Server supported only built-in aggregates. Custom aggregate functionality was achieved using cursors, which was rather cumbersome. With CLR integration, you can now create your own custom aggregate functions. CLR integration is the only way to create custom aggregates in SQL Server. CLR aggregates are simpler, and offer better performance than cursors. Performance of custom aggregates compares to performance of built-in aggregates.

The ability to create custom aggregates is very powerful. For example, imagine being able to aggregate a list of binary photographs stored in the database as an image data type and determine which one has the most likeness to a specific photograph you pass to it. In the case of spatial data, you can determine the total length of a route around all those points, or the shortest route. You could also implement complex statistical analysis with functions unavailable in SQL Server out of the box, or you could simply concatenate string values from a varchar column.

In order to create a user-defined aggregate, your class must adhere to the aggregation contract. The aggregation contract consists of the following requirements:

  • Mark your class with SqlUserDefinedAggregate.

  • Create an instance method called Init. This method initializes the aggregation. Here, you will reset any variables that may have been set in a previous use, and instantiate any variables you will use during the aggregation.

  • Create an instance method called Accumulate. It should accept one parameter: the value that is being accumulated. SQL Server calls this method for every value in the group being processed. You should use this method to update the state of your aggregate as the value is accumulated.

  • Create an instance method called Merge. It should accept one parameter: another instance of your aggregate class. This method is used to merge multiple aggregations.

  • Create an instance method called Terminate. This method should accept no parameters, but it should return the final result of your aggregation.

Table 2 lists all properties that apply to the SqlUserDefinedAggregate custom attribute.

Table 2. SqlUserDefinedAggregate Custom Attribute Properties
PropertyExplanation
FormatFormat of serializing the aggregate to a string. This can be either native or user-defined.
IsInvariantToDuplicatesDetermines whether the function ignores duplicate values. For example, the MIN and MAX aggregates ignore duplicate values. The SUM aggregate does not ignore duplicate values and will return a very different result when the same value is passed to it multiple times.
IsInvariantToNullsDetermines if the function ignores null values. For example, the SUM function will return the same value when null values are passed, while the COUNT function will not.
IsInvariantToOrderThis property is not used by SQL Server at this point. It is reserved for future use.
IsNullIfEmptyDetermines whether the aggregate will return null if no values have been passed to it.
MaxByteSizeThe amount of memory in bytes your aggregate will consume during processing. The maximum value is 2GB. Specify between 1 to 8000 bytes, or–1 if your aggregate will consume over 8000 bytes (but less than 2GB).

Examples 6 and 7 show how we can list text values all on one line, separating them by a semicolon. This can be very useful when you have a column storing e-mail addresses and you want to list them all on one line with separators (and then perhaps send it to your e-mail system to send an e-mail to all those addresses).

Example 6. Implementing ListWithSeparator—C#
using System;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Text;
using System.IO;
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
IsInvariantToNulls = true,
IsInvariantToDuplicates = true,
IsInvariantToOrder = true,
MaxByteSize = -1)
]
public class ListWithSeparator : IBinarySerialize
{
// Comment: the tempStringBuilder variable holds the list while the
aggregation is being processed
public StringBuilder tempStringBuilder;
public void Init()
{
tempStringBuilder = new StringBuilder();
}
public void Accumulate(SqlString value)
{
if (!value.IsNull)
{
tempStringBuilder.Append(value.Value);
tempStringBuilder.Append(";");
}
}
public void Merge(ListWithSeparator anotherList)
{
tempStringBuilder.Append(anotherList.tempStringBuilder.ToString());
}
public SqlString Terminate()
{
if (tempStringBuilder != null
&& tempStringBuilder.Length > 0)
{
return new SqlString(tempStringBuilder.ToString());
}
return new SqlString(string.Empty);
}
public void Read(BinaryReader reader)
{
tempStringBuilder = new StringBuilder(reader.ReadString());
}
public void Write(BinaryWriter writer)
{
writer.Write(tempStringBuilder.ToString());
}
}


Example 7. Implementing ListWithSeparator—Visual Basic .NET
Imports System
Imports System.Data
Imports Microsoft.SqlServer.Server
Imports System.Data.SqlTypes
Imports System.Text
Imports System.IO
<Serializable(), SqlUserDefinedAggregate(Format.UserDefined,
IsInvariantToNulls:=True, IsInvariantToDuplicates:=True,
IsInvariantToOrder:=True, MaxByteSize:=-1)> _
Public Class ListWithSeparator
Implements IBinarySerialize
Private tempStringBuilder As StringBuilder
Public Sub Init()
tempStringBuilder = New StringBuilder()
End Sub
Public Sub Accumulate(ByVal value As SqlString)
If value.IsNull Then
Return
End If
tempStringBuilder.Append(value.Value).Append(","c)
End Sub
Public Sub Merge(ByVal anotherList As ListWithSeparator)
tempStringBuilder.Append(anotherList.tempStringBuilder)
End Sub
Public Function Terminate() As SqlString
If Not (tempStringBuilder Is Nothing) AndAlso tempStringBuilder.
Length > 0 Then
return New SqlString(tempStringBuilder.ToString())
End If
Return New SqlString(String.Empty)
End Function
Public Sub Read(reader As BinaryReader)
tempStringBuilder = New StringBuilder(reader.ReadString())
End Sub
Public Sub Write(writer As BinaryWriter)
writer.Write(Me.intermediateResult.ToString())
End Sub
End Class


Tip

Quickly revise the five requirements that make up a custom aggregate contract. These are: use the SqlUserDefinedAggregate attribute, and implement Init, Accumulate, Merge, and Terminate methods. You don’t need to memorize the actual syntax for doing this.


Other -----------------
- Encryption Catalog Views
- Built-In Cryptographic Functions
- SQL server 2008 : Managing Security - Permissions
- SQL server 2008 : Managing Security - Schemas
- SQL server 2008 : Managing Security - Users
- SQL server 2008 : Managing Security - Roles
- SQL Server 2008 : Managing Remote Servers
- Linked Servers
- Adding, Dropping, and Configuring Linked Servers
- Mapping Local Logins to Logins on Linked Servers
- Obtaining General Information About Linked Servers
- Executing a Stored Procedure via a Linked Server
- Setting Up Linked Servers Using SQL Server Management Studio
- Encryption basics for SQL Server : Cryptographic Keys
- Encryption basics for SQL Server : Key Maintenance
- Encryption basics for SQL Server : Key Algorithms
- SQL Server 2005 : Performing Database Backups
- SQL Server 2005 : Restoring Data from a Backup
- SQL Server 2005 : Using Database Snapshots
- SQL Server 2005 : Automating Maintenance with Job Scheduling
 
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
programming4us programming4us